#include "log.h"
#include "volatile_queue.h"

volatile_queue_t log_queue = 
{
    .empty_flag = 1,
    .head = 0,
    .tail = 0,
};


uint32_t sector_offset = 0;
uint32_t sector_number = 0;
uint16_t current_log_sn = 0;

static uint8_t log_read( uint32_t address, uint8_t* buffer,  uint32_t length )
{
    return W25Q32_Read(address,buffer,length);
}

static uint8_t log_write( uint32_t address, uint8_t* buffer,  uint32_t length )
{
    return W25Q32_Write(address,buffer,length);
}

static uint8_t log_is_busy(void)
{
    return W25Q32_ReadStatus();
}
uint32_t address = 0;
log_header_t header;
uint8_t log_ret = 0;
uint8_t log_init(void)
{
    uint16_t i = 0;
    uint32_t sn = 0;
    log_ret = 0;
    for(i = 0;i < SECTOR_CNT;i++)
    {
        address = i * SECTOR_SIZE;

        log_ret = log_read(address,(uint8_t *)&header,sizeof(log_header_t));
        //read log
        
        if( header.magic_number != LOG_MAGIC_NUNMBER )
        {
            if( header.magic_number == 0xffffffff )
            {
                sector_number = i;
                address = i * SECTOR_SIZE;
            }
            else
            {
                //invalid data,erase all chip and wait finish
                log_ret += W25Q32_ChipErase(1);
                sector_number = 0;
                address = 0;
            }

            sector_offset += sizeof(log_header_t);
            header.log_sn = sn+1;
            header.magic_number = LOG_MAGIC_NUNMBER;
            current_log_sn = header.log_sn;
            log_ret = log_write(address,(uint8_t *)&header,sizeof(log_header_t));
            break;
            //mark the current sector
        }
        else
        {
            //record latest log sequence number
            sn = header.log_sn;
        }
    }

    if( i == SECTOR_CNT )
    {
        //all sector is used，erase first sector
        address = 0;
        log_ret += W25Q32_SectorErase(address,1);
        header.log_sn = sn+1;
        current_log_sn = header.log_sn;
        header.magic_number = LOG_MAGIC_NUNMBER;
        log_ret = log_write(address,(uint8_t *)&header,sizeof(log_header_t));
        sector_offset += sizeof(log_header_t);
    }
    return log_ret;
}



void status_log_inqueue(void *pack)
{
    if( pack == NULL )
    {
        return;
    }
    status_log_pack_t *status = (status_log_pack_t*)pack;
    status_log_t log;

    log.header.block_header = BLOCK_HEADER;
    log.header.data_type = LOG_DATA_TYPE_STATUS;
    log.header.data_length = sizeof(status_log_pack_t);
    memcpy(&(log.pack),status,log.header.data_length);
    volatile_inqueue( &log_queue,(uint8_t*)&log,sizeof(status_log_t));
}



void log_task(void)
{
    uint8_t buf[MAX_QUEUE_PACK_SIZE]={0};
    uint32_t buf_len = 0;
    uint32_t address;
    uint32_t sector_remain = SECTOR_SIZE - sector_offset;

    buf_len = volatile_get_next_element_size(&log_queue);
    if( buf_len != 0 && log_is_busy() == 0)
    {
        //queue is not empty and flash is ready
        if( sector_remain == SECTOR_SIZE )
        {
            log_header_t header;
            header.log_sn = current_log_sn;
            header.magic_number = LOG_MAGIC_NUNMBER;
            log_write(address,(uint8_t *)&header,sizeof(log_header_t));
            sector_offset += sizeof(log_header_t);
        }
        else if( sector_remain >= buf_len )
        {
            buf_len = volatile_dequeue( &log_queue,buf);
            address = sector_offset + sector_number * SECTOR_SIZE;
            log_write( address,buf,buf_len);
            sector_offset += buf_len;    
        }
        else
        {
            sector_number += 1;
            sector_offset = 0;
            address = sector_offset + sector_number*SECTOR_SIZE;
            W25Q32_SectorErase(address,0);
        }
    }
}

